/*

Gamebots UT Copyright (c) 2002, Andrew N. Marshal, Gal Kaminka
Gamebots UT 2003/2004 port Copyright (c) 2005
       Joe Manojlovich, Tim Garwood, Jessica Bayliss
Gamebots Pogamut derivation Copyright (c) 2007, Michal Bida
	- derived mainly from 2002 Marshal, Kaminka version

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software must also be in compliance with the Epic Games Inc. license for mods which states the following: "Your mods must be distributed solely for free, period. Neither you, nor any other person or party, may sell them to anyone, commercially exploit them in any way, or charge anyone for receiving or using them without prior written consent of Epic Games Inc. You may exchange them at no charge among other end-users and distribute them to others over the Internet, on magazine cover disks, or otherwise for free." Please see http://www.epicgames.com/ut2k4_eula.html for more information.

*/


//=============================================================================
// DeathMatchPlus.
//=============================================================================
class BotDeathMatch extends DeathMatch
	config(BotAPI);


#exec OBJ LOAD FILE=TeamSymbols_UT2003.utx
#exec OBJ LOAD FILE=TeamSymbols.utx				// needed right now for Link symbols, etc.
#exec OBJ LOAD File=AnnouncerMain.uax


var ControlServer	theControlServer;
var MapVisServer	theMapVisServer;
var config bool		bAllowControlServer;
var BotServer		theBotServer;
var bool		bServerLoaded, bBoolResult;
var RemoteBotInfo	RemoteBotConfig;
var int			NumRemoteBots;
var string GameClass;

//This is used for logging used ports for bot and control server
var FileLog Logger;

var config int BotServerPort;
var config int ControlServerPort;

//every damage the bots receive will be multiplied by this modifier
var config float DamageModifier;

//maximum bot skill that can be set in this game
var config int MaxSkill;

//if true we will pick some random free ports for bot and control server and write
//them down to a file in UserLog directory (name is set in LoggerFileName)
var config bool bRandomPorts;

//if set to true all weapons from map will be erased
var bool bShouldEraseAllWeapons;

//here we store if our connections to bot and cotrol server are protected by pass
//will force a bit changed initial protocol (to check password)
var bool bPasswordProtected;

//here we store Password for bot and control connections
var string Password;

//who initiated the password protection
var string PasswordByIP;

// This class is used for pausing the game. We supply it to Level.Pauser variable.
var PauserFeed LevelPauserFeed;
//Here we store all available maps
var array<string> Maps;

//this function is called even before PreBeginPlay, we parse GB parameters from
//command line here
event InitGame(string Options, out string Error )
{
	local string InOpt;
	local Mutator M;
	local bool bFound;

	super.InitGame(Options, Error);

	bFound = false;
	//Make sure the GBHUDMutator is on!
	for (M = BaseMutator; M != None; M = M.NextMutator)
	{
    	if (M.IsA('GBHudMutator'))
    	{
    		bFound = true;
			break;
		}

	}
	if (!bFound)
		AddMutator("BotAPI.GBHudMutator");

	BotServerPort = Clamp(GetIntOption( Options, "BotServerPort", BotServerPort ),2000,32000);
	ControlServerPort = Clamp(GetIntOption( Options, "ControlServerPort", ControlServerPort ),2000,32000);

	InOpt = ParseOption( Options,"bRandomPorts");
	if ( InOpt != "" )
		bRandomPorts = bool(InOpt);

	InOpt = ParseOption( Options, "Password");
	if (InOpt != "")
	{
	    bPasswordProtected = true;
	    PasswordByIP = Level.GetAddressURL();
	    Password = InOpt;
	}
	else
	{
		bPasswordProtected = false;
	}

}

//This function is automaticaly called after beginning of the game
function PostBeginPlay()
{
	Super.PostBeginPlay();

	if(!bServerLoaded)
    {
		if (bAllowControlServer)
        {
			theControlServer = spawn(class'ControlServer',self);
		}

		theBotServer = Spawn(class'BotServer',self);
		//theMapVisServer = Spawn(class'MapVisServer',self); //this is not used yet

		bServerLoaded = true;
	}
	else
	{

		theBotServer.bClosed = false;
		theBotServer.Listen();

		theControlServer.bClosed = false;
		theControlServer.Listen();
	}
	//HACK? Set to true, so the match will start imediatelly
	//We should implement support for StartMatch function...
	bQuickStart = true;

	RemoteBotConfig = Spawn(class'RemoteBotInfo');
	LevelPauserFeed = Spawn(class'BotAPI.PauserFeed');
	LoadMapsFromPrefix(MapPrefix);
	RemoteBotConfig.Difficulty = AdjustedDifficulty;

	Log("GB server on.");
	Log("ControlServerPort:"$theControlServer.ListenPort$" BotServerPort:"$theBotServer.ListenPort);
	//Test();

}

function Test()
{
	local AIMarker A;
	local UnrealScriptedSequence U;

	ForEach AllActors( class 'AIMarker', A,  )
	{
		log("Ai marker is "$A);
		log("rotation is "$A.Rotation);
	}

	ForEach AllActors( class 'UnrealScriptedSequence', U,  )
	{
		log("Sequence is "$U);
		log("rotation is "$U.Rotation);
		log("AI marker rotation is "$U.myMarker.Rotation);
	}

}

function StartMatch()
{
	local UTWeaponPickup A;

	if (bShouldEraseAllWeapons == true)
	{
		ForEach DynamicActors( class 'xWeapons.UTWeaponPickup', A,  )
		{
			A.GotoState('Disabled');
		}
	}

	super.StartMatch();

}

//This function returns text information about game
function string GetGameInfo()
{
	local string ib, as, ae;
	local string outStr;

	ib = theBotServer.ib;
	as = theBotServer.as;
	ae = theBotServer.ae;

	outStr = ( " {FragLimit " $ GoalScore $
		"} {TimeLimit " $ TimeLimit $
		"}");

	return outStr;
}

//Returns game status - list of players and their score
function string GetGameStatus(optional RemoteBot theBot)
{
	local string ib, as, ae;
	local string outStr;
	local Controller C;

	ib = theBotServer.ib;
	as = theBotServer.as;
	ae = theBotServer.ae;

	outStr = "GAM";

	outStr = ( outStr $ " {DamageModifier " $ DamageModifier $
		"} {PlayerScores" );
	for ( C=Level.ControllerList; C!=None; C=C.NextController )
	{
		if( (C.IsA('RemoteBot') || C.IsA('GBxPlayer') || C.IsA('GBxBot')) && !C.IsA('Spectator') )
			outStr = ( outStr $" {"$ C $" "$ int(C.PlayerReplicationInfo.Score) $"}" );
	}
	outStr = ( outStr $"}" );

	return outStr;
}

function LoadMapsFromPreFix(string Prefix)
{
   local string FirstMap,NextMap,MapName,TestMap;
   local int i, z;

   FirstMap = Level.GetMapName(PreFix, "", 0);
   NextMap = FirstMap;
   i = 0;
   while(!(FirstMap ~= TestMap))
   {
      MapName = NextMap;
      z = InStr(Caps(MapName), ".UT2");
      if(z != -1)
         MapName = Left(MapName, z);  // remove ".UT2"

      Maps[i] = MapName;

      NextMap = Level.GetMapName(PreFix, NextMap, 1);
      TestMap = NextMap;
      i += 1;
   }
}

//Used for restarting our RemoteBots
function RemoteRestartPlayer( Controller aPlayer, optional vector startLocation, optional rotator startRotation )
{
	log("We are in RemoteRestartPlayer");

	SpawnPawn( RemoteBot(aPlayer), startLocation, startRotation);

}

//Rewriten so we can set the position and respawn playercontrollers
function RestartPlayer(Controller aPlayer)
{

    local NavigationPoint startSpot;
    local int TeamNum;
    local class<Pawn> DefaultPlayerClass;
	local Vehicle V, Best;
	local vector ViewDir;
	local float BestDist, Dist;

	//for remote bot we have different function (SpawnPawn)
	if (aPlayer.IsA('RemoteBot'))
		return;

	if (aPlayer.IsA('GBxPlayer'))
	{

	    if( bRestartLevel && Level.NetMode!=NM_DedicatedServer && Level.NetMode!=NM_ListenServer )
        	return;

	    if ( (aPlayer.PlayerReplicationInfo == None) || (aPlayer.PlayerReplicationInfo.Team == None) )
    	    TeamNum = 255;
	    else
    	    TeamNum = aPlayer.PlayerReplicationInfo.Team.TeamIndex;

		if (GBxPlayer(aPlayer).NextStartLocation == vect(0,0,0))
		{
		    startSpot = FindPlayerStart(aPlayer, TeamNum);
		    if( startSpot == None )
		    {
        		log(" Player start not found!!!");
		        return;
		    }
		}

		if (aPlayer.PreviousPawnClass!=None && aPlayer.PawnClass != aPlayer.PreviousPawnClass)
	        BaseMutator.PlayerChangedClass(aPlayer);

    	if ( aPlayer.PawnClass != None )
    	{
    		if (GBxPlayer(aPlayer).NextStartLocation != vect(0,0,0))
    		{
	        	aPlayer.Pawn = Spawn(aPlayer.PawnClass,,,GBxPlayer(aPlayer).NextStartLocation,GBxPlayer(aPlayer).NextStartRotation);
	        	GBxPlayer(aPlayer).NextStartLocation = vect(0,0,0);
	        	GBxPlayer(aPlayer).NextStartRotation = rot(0,0,0);
	        }
	        else
		        aPlayer.Pawn = Spawn(aPlayer.PawnClass,,,StartSpot.Location,StartSpot.Rotation);
        }

	    if( aPlayer.Pawn==None )
    	{
        	DefaultPlayerClass = GetDefaultPlayerClass(aPlayer);
    		if (GBxPlayer(aPlayer).NextStartLocation != vect(0,0,0))
    		{
	        	aPlayer.Pawn = Spawn(DefaultPlayerClass,,,GBxPlayer(aPlayer).NextStartLocation,GBxPlayer(aPlayer).NextStartRotation);
	        	GBxPlayer(aPlayer).NextStartLocation = vect(0,0,0);
	        	GBxPlayer(aPlayer).NextStartRotation = rot(0,0,0);
	        }
	        else
		        aPlayer.Pawn = Spawn(DefaultPlayerClass,,,StartSpot.Location,StartSpot.Rotation);
    	}
	    if ( aPlayer.Pawn == None )
    	{
    		if (StartSpot != none)
	        	log("Couldn't spawn player of type "$aPlayer.PawnClass$" at "$StartSpot);
	        aPlayer.GotoState('Dead');
	        if ( PlayerController(aPlayer) != None )
				PlayerController(aPlayer).ClientGotoState('Dead','Begin');
        	return;
    	}

	    if ( PlayerController(aPlayer) != None )
			PlayerController(aPlayer).TimeMargin = -0.1;
		if (StartSpot != none)
		{
		    aPlayer.Pawn.Anchor = startSpot;
			aPlayer.Pawn.LastStartSpot = PlayerStart(startSpot);
		}
		aPlayer.Pawn.LastStartTime = Level.TimeSeconds;
    	aPlayer.PreviousPawnClass = aPlayer.Pawn.Class;

	    aPlayer.Possess(aPlayer.Pawn);
    	aPlayer.PawnClass = aPlayer.Pawn.Class;

	    aPlayer.Pawn.PlayTeleportEffect(true, true);
    	aPlayer.ClientSetRotation(aPlayer.Pawn.Rotation);
	    AddDefaultInventory(aPlayer.Pawn);
   		if (StartSpot != none)
	    	TriggerEvent( StartSpot.Event, StartSpot, aPlayer.Pawn);

	    if ( bAllowVehicles && (Level.NetMode == NM_Standalone) && (PlayerController(aPlayer) != None) )
    	{
			// tell bots not to get into nearby vehicles for a little while
			BestDist = 2000;
			ViewDir = vector(aPlayer.Pawn.Rotation);
			for ( V=VehicleList; V!=None; V=V.NextVehicle )
				if ( V.bTeamLocked && (aPlayer.GetTeamNum() == V.Team) )
				{
					Dist = VSize(V.Location - aPlayer.Pawn.Location);
					if ( (ViewDir Dot (V.Location - aPlayer.Pawn.Location)) < 0 )
						Dist *= 2;
					if ( Dist < BestDist )
					{
						Best = V;
						BestDist = Dist;
					}
				}

			if ( Best != None )
				Best.PlayerStartTime = Level.TimeSeconds + 8;
		}


	}
	else
	{
		super.RestartPlayer(aPlayer);
	}

}

/*
function AddBotToList(RemoteBot newBot)
{
	local int i;

	for (i=0; i < 32; i++)
	{
		if (RemoteBots[i] == none)
			break;
	}
	if (i <= 31)
	{
		RemoteBots[i] = newBot;
	}
}

function RemoveBotFromList(RemoteBot newBot)
{
	local int i;

	for (i=0; i < 32; i++)
	{
		if (RemoteBots[i] == newBot)
		{
		 	RemoteBots[i] = none;
		 	break;
		}
	}
}*/

//Main function for adding bot to the game (creates also controller)
function RemoteBot AddRemoteBot
(
	BotConnection theConnection,
	string clientName,
	int TeamNum,
	optional string className,
	optional string DesiredSkin,
	optional float DesiredSkill,
	optional float DesiredAccuracy,
	optional bool ShouldLeadTarget
)
{
	local RemoteBot NewBot;
	local GBReplicationInfo repInfo;


	//I dont think location here is necessary. Its just controller class
	NewBot = Spawn(class'BotAPI.RemoteBot',self,,,);

	if ( NewBot == None )
	{
		log("In AddRemoteBot() - Cant spawn RemoteBot ");
		return None;
	}

	//AddBotToList(NewBot); //Test

	//hook up connection to socket
	NewBot.myConnection = theConnection;
	NewBot.as = theConnection.as;
	NewBot.ae = theConnection.ae;
	NewBot.ib = theConnection.ib;

	NewBot.bIsPlayer = true;
	NewBot.bHidden = false;

	if (DesiredSkin != "")
		NewBot.DesiredSkin = DesiredSkin;
	else
	    NewBot.DesiredSkin = "ThunderCrash.JakobM";

	// Set the player's ID.
	NewBot.PlayerReplicationInfo.PlayerID = CurrentID++;

	// Add custom GBReplicationInfo
	repInfo = class'GBReplicationInfo'.Static.SpawnFor(NewBot.PlayerReplicationInfo);
	repInfo.MyPRI = NewBot.PlayerReplicationInfo;


	//Increase numbers properly, so no epic bot will join the game
	MinPlayers = Max(MinPlayers+1, NumPlayers + NumBots + 1);
	NumRemoteBots++;
	NumPlayers++;

	if ( clientName != "" )
	{
		NewBot.PlayerReplicationInfo.PlayerName = clientName;
		changeName( newBot, clientName, true );
	}

	if (!AddRemoteBotToTeam(NewBot,TeamNum))
	{
		log("In AddRemoteBot() - In Team Game Bot without team!");
		//NewBot.Destroy();
		//return None;
	}

	//Here we set values of the bot difficulty, it affects how good he will be
    //TODO: Really here?! But where then?
    //Maximum is 1.0
    if ( (DesiredAccuracy > 0) && (DesiredAccuracy <= 1) )
		Newbot.Accuracy = DesiredAccuracy;
	else
		NewBot.Accuracy = 1.0;

	//Turns on strafing ability
	Newbot.StrafingAbility = 1.0;
	//limit maximum bot skill
	if (DesiredSkill > MaxSkill)
		DesiredSkill = MaxSkill;
    //From 0 to 7
    if ((DesiredSkill > 0) && (DesiredSkill <= 7))
    	Newbot.Skill = DesiredSkill;
    else
		Newbot.Skill = 7;
	//Shooting ahead of targets - disabled? - aiming is thing of a client
	NewBot.bLeadTarget = ShouldLeadTarget;

	//Need to Spawn PawnClass, default should work , but for now we will be using just xPawn
	if(className != "")
	{
		NewBot.PawnClass = class<Pawn>(DynamicLoadObject(className, class'Class'));
	}
	else
	{
		if(newBot.PawnClass == None)
		{
			log("newBot.PawnClass is None");
			newBot.PawnClass = class<Pawn>(DynamicLoadObject("BotAPI.GBxPawn", class'Class'));
		}
	}

	//We will let the bots know that new bots came to server
	RemoteNotifyLoging(newBot);

	return NewBot;
}

//Here we spawn and respawn the bot Pawn - thats the visible avatar of the bot
function SpawnPawn
(
	RemoteBot NewBot,
	optional vector startLocation,
	optional rotator startRotation
)
{
	local NavigationPoint startSpot;

	if (NewBot == None)
	{
    	log("In SpawnPawn(), - NewBot is None! ");
    	return;
	}

	if (NewBot.Pawn != None)
	{
		log("In SpawnPawn(), - "$NewBot$" Pawn already spawned ");
		return;
	}

	if ( StartLocation == vect(0,0,0) && StartRotation == rot(0,0,0) )
	{
		StartSpot = FindPlayerStart(NewBot);
    	newBot.Pawn = Spawn(NewBot.PawnClass,newBot,,StartSpot.Location,StartSpot.Rotation);
	}
	else
	{
		if (StartLocation != vect(0,0,0) && StartRotation != rot(0,0,0))
		{
			NewBot.Pawn = Spawn(NewBot.PawnClass,newBot,,StartLocation,StartRotation);
		}
		else
		{
			if (StartRotation != rot(0,0,0))
			{
				StartSpot = FindPlayerStart(NewBot);
				newBot.Pawn = Spawn(NewBot.PawnClass,newBot,,StartSpot.Location,StartRotation);
			}
			else  //this means StartLocation != vect(0,0,0)
			{
				newBot.Pawn = Spawn(NewBot.PawnClass,newBot,,StartLocation, );
			}
		}

	}

	if (NewBot.Pawn == None)
	{
    	log("In SpawnPawn() - Cant spawn the pawn of bot "$NewBot);
		return;
	}

    newBot.Pawn.Controller = newBot;
	GBxPawn(NewBot.Pawn).SetSkin(NewBot.DesiredSkin);//, NewBot.myConnection.BotSkin);

	//Taken from Spawning class of Epic bots
	if (StartSpot != none) //TODO: Find here nearest NavPoint?
	{
    	NewBot.Pawn.Anchor = startSpot;
		NewBot.Pawn.LastStartSpot = PlayerStart(startSpot);
	}
	NewBot.Pawn.LastStartTime = Level.TimeSeconds;
    NewBot.PreviousPawnClass = NewBot.Pawn.Class;

    NewBot.Possess(NewBot.Pawn);
    NewBot.PawnClass = NewBot.Pawn.Class;

    NewBot.Pawn.PlayTeleportEffect(true, true);
    //NewBot.ClientSetRotation(aPlayer.Pawn.Rotation); //We want to preserve our rotation
    if (!bShouldEraseAllWeapons)
    	AddDefaultInventory(NewBot.Pawn);

    if (StartSpot != none)
    	TriggerEvent( StartSpot.Event, StartSpot, NewBot.Pawn );

   	//Setting some initial Pawn properties
	Newbot.Pawn.PeripheralVision = -0.3;
	Newbot.Pawn.bAvoidLedges = false;
	Newbot.Pawn.bStopAtLedges = false;
	Newbot.Pawn.bCanJump = true;


 	// broadcast a welcome message.
	BroadcastLocalizedMessage(GameMessageClass, 1, NewBot.PlayerReplicationInfo);

	NewBot.GotoState('StartUp', 'DoStop'); //TODO: Really here?
}

function bool AddRemoteBotToTeam(Bot NewBot, int TeamNum)
{
	return true;
}

//This function is called, when somebody on the server dies.
function Killed( Controller Killer, Controller Killed, Pawn KilledPawn, class<DamageType> damageType )
{
	local Controller C;

	Super.Killed(Killer, Killed, KilledPawn, damageType);

	//Send DIE message to killed bot
	if( Killed.isA('RemoteBot'))
	{
		RemoteBot(Killed).RemoteDied(Killer, damageType);
    }

	//Send KIL message to other bots
	for(C = Level.ControllerList; C != none; C = C.NextController)
	{
		if( C.isA('RemoteBot') && C != Killed )
		{
			RemoteBot(C).RemoteKilled(Killer, Killed, damageType);
		}
	}
}

//We get a team for our epic bot here
//this function should not be called in BotDeathMatch
function UnrealTeamInfo GetEpicBotTeam(optional int TeamNumber)
{
	log("EpicGetBotTeam() - In BotDeathMatch - should not be called");
	return None;
}

//Next two functions handles spawning of epic bot, sometimes we could want them
//running in our game
//A lot of code collected from a lot of ingame classes acros the game types and so
function bool AddEpicBot(
	optional string BotName,
	optional int TeamNumber,
	optional vector StartLocation,
	optional rotator StartRotation,
	optional float skill
)
{
	local Bot NewBot;
	local RosterEntry Chosen;
	local UnrealTeamInfo BotTeam;
	local GBReplicationInfo repInfo;


	MinPlayers = Max(MinPlayers+1, NumPlayers + NumBots + 1);

	if ( Level.Game.IsA('BotTeamGame') )
	{
		BotTeam = GetEpicBotTeam(TeamNumber);
	}
	else
	{
		BotTeam = GetBotTeam();
	}

	Chosen = BotTeam.ChooseBotClass("");

	if (Chosen.PawnClass == None)
		Chosen.Init(); //amb
	// log("Chose pawn class "$Chosen.PawnClass);
	NewBot = Spawn(class'BotAPI.GBxBot');

	if ( NewBot != None )
	{

    	if (skill > MaxSkill)
    		skill = MaxSkill;

		if (skill > 0 && skill <= 7)
			NewBot.InitializeSkill(skill);
		else
			NewBot.InitializeSkill(AdjustedDifficulty);
		Chosen.InitBot(NewBot);

		BotTeam.AddToTeam(NewBot);


		if ( BotName != "" )
			ChangeName(NewBot, BotName, false);
		else
			ChangeName(NewBot, Chosen.PlayerName, false);

		NewBot.StrafingAbility = 1;
		if ( bEpicNames && (NewBot.PlayerReplicationInfo.PlayerName ~= "The_Reaper") )
		{
			NewBot.Accuracy = 1;
			NewBot.StrafingAbility = 1;
			NewBot.Tactics = 1;
			NewBot.InitializeSkill(AdjustedDifficulty+2);
		}
		BotTeam.SetBotOrders(NewBot,Chosen);
	}

	if ( NewBot == None )
	{
		warn("Failed to spawn bot.");
		return false;
	}

	// broadcast a welcome message.
	BroadcastLocalizedMessage(GameMessageClass, 1, NewBot.PlayerReplicationInfo);

	NewBot.PlayerReplicationInfo.PlayerID = CurrentID++;
	NumBots++;

	// Add custom GBReplicationInfo
	repInfo = class'GBReplicationInfo'.Static.SpawnFor(NewBot.PlayerReplicationInfo);
	repInfo.MyPRI = NewBot.PlayerReplicationInfo;


	RemoteNotifyLoging(newBot);

	SpawnEpicBot(NewBot,StartLocation,StartRotation);

	return true;

}

//Here we spawn a Pawn for our epic bot, it is used just for the first spawning
//restart of the bot is handled by RestartPlayer inherited from DeathMatch
function SpawnEpicBot(Controller aPlayer, optional vector StartLocation, optional rotator StartRotation)
{
	local NavigationPoint startSpot;
	local class<Pawn> DefaultPlayerClass;
	local Vehicle V, Best;
	local vector ViewDir;
	local float BestDist, Dist;

    if( bRestartLevel && Level.NetMode!=NM_DedicatedServer && Level.NetMode!=NM_ListenServer )
        return;

    if (StartLocation == vect(0,0,0))
	{
		startSpot = FindPlayerStart(aPlayer);
		if( startSpot == None )
    	{
        	log(" Player start not found!!!");
        	return;
    	}
		StartLocation = startSpot.Location;
		StartRotation = startSpot.Rotation;
	}


    if (aPlayer.PreviousPawnClass!=None && aPlayer.PawnClass != aPlayer.PreviousPawnClass)
        BaseMutator.PlayerChangedClass(aPlayer);

   // if ( aPlayer.PawnClass != None )
   aPlayer.PawnClass = class'BotAPI.GBxPawn';
   aPlayer.Pawn = Spawn(aPlayer.PawnClass,,,StartLocation,StartRotation);

    if( aPlayer.Pawn==None )
    {
        DefaultPlayerClass = GetDefaultPlayerClass(aPlayer);
        aPlayer.Pawn = Spawn(DefaultPlayerClass,,,StartLocation,StartRotation);
    }
    if ( aPlayer.Pawn == None )
    {
        log("Couldn't spawn player of type "$aPlayer.PawnClass$" at "$StartSpot);
        aPlayer.GotoState('Dead');
        if ( PlayerController(aPlayer) != None )
			PlayerController(aPlayer).ClientGotoState('Dead','Begin');
        return;
    }
    if ( PlayerController(aPlayer) != None )
		PlayerController(aPlayer).TimeMargin = -0.1;

	if (startSpot != none) //TODO: Find nearest navpoint?
	{
    	aPlayer.Pawn.Anchor = startSpot;
		aPlayer.Pawn.LastStartSpot = PlayerStart(startSpot);
	}
	aPlayer.Pawn.LastStartTime = Level.TimeSeconds;
    aPlayer.PreviousPawnClass = aPlayer.Pawn.Class;

    aPlayer.Possess(aPlayer.Pawn);
    aPlayer.PawnClass = aPlayer.Pawn.Class;

    aPlayer.Pawn.PlayTeleportEffect(true, true);
    aPlayer.ClientSetRotation(aPlayer.Pawn.Rotation);

    if (!bShouldEraseAllWeapons)
	    AddDefaultInventory(aPlayer.Pawn);

    if (startSpot != none)
	    TriggerEvent( StartSpot.Event, StartSpot, aPlayer.Pawn); //startSpot can be None!

    if ( bAllowVehicles && (Level.NetMode == NM_Standalone) && (PlayerController(aPlayer) != None) )
    {
		// tell bots not to get into nearby vehicles for a little while
		BestDist = 2000;
		ViewDir = vector(aPlayer.Pawn.Rotation);
		for ( V=VehicleList; V!=None; V=V.NextVehicle )
			if ( V.bTeamLocked && (aPlayer.GetTeamNum() == V.Team) )
			{
				Dist = VSize(V.Location - aPlayer.Pawn.Location);
				if ( (ViewDir Dot (V.Location - aPlayer.Pawn.Location)) < 0 )
					Dist *= 2;
				if ( Dist < BestDist )
				{
					Best = V;
					BestDist = Dist;
				}
			}

		if ( Best != None )
			Best.PlayerStartTime = Level.TimeSeconds + 8;
	}
}

//Called when new human player enters the game
event PlayerController Login( string Portal, string Options, out string Error )
{
	local PlayerController Loging;
	local GBReplicationInfo repInfo;


	Loging = super.Login( Portal, Options, Error );

	//Add our custom replication info

	repInfo = class'GBReplicationInfo'.Static.SpawnFor(Loging.PlayerReplicationInfo);
	repInfo.MyPRI = Loging.PlayerReplicationInfo;


	RemoteNotifyLoging(Loging);

	return Loging;

}

//We send a notification to our remote bots, that new player joined the server
function RemoteNotifyLoging( Controller Loging )
{
	local GBClientClass A;
	local string outstring;
	local string ib,as,ae;

	ib = theBotServer.ib;
	as = theBotServer.as;
	ae = theBotServer.ae;

	outstring = "JOIN" $ib$as$
		"Id" $ib$ Loging$Loging.PlayerReplicationInfo.PlayerID $ae$ib$as$
		"Name"$ib$Loging.PlayerReplicationInfo.PlayerName$ae;

   	//Finding any GBClientClass for sending the message
	A = theBotServer.ChildList;

	if (A == none)
	{
		if (theControlServer != None)
		{
			A = theControlServer.ChildList;
			if (A != none)
			{
				A.GlobalSendLine(outstring,true,true);
			}
		}
	}
	else
	{
		A.GlobalSendLine(outstring,true,true);
	}


}

//Notification about player leaving the server
function RemoteNotifyLogout(Controller Exiting)
{
	local GBClientClass A;
	local string outstring;
	local string ib,as,ae;

	ib = theBotServer.ib;
	as = theBotServer.as;
	ae = theBotServer.ae;

	outstring = "LEFT"$ib$as$
		"Id"$ib$Exiting$Exiting.PlayerReplicationInfo.PlayerID$ae$ib$as$
		"Name"$ib$Exiting.PlayerReplicationInfo.PlayerName$ae;

    //Finding any GBClientClass for sending the message
	A = theBotServer.ChildList;

	if (A == none)
	{
		if (theControlServer != None)
		{
			A = theControlServer.ChildList;
			if (A != none)
				A.GlobalSendLine(outstring,true,true);
		}
	}
	else
	{
		A.GlobalSendLine(outstring,true,true);
	}
}

//Called when somebody (bot/player) leaves game
function Logout(controller Exiting)
{
	RemoteNotifyLogout(Exiting);
	if(!exiting.IsA('RemoteBot'))
	{
		Super.Logout(Exiting);
	}
	else
	{
		//RemoveBotFromList(RemoteBot(Exiting)); //test
		Super(GameInfo).Logout(Exiting);
		NumRemoteBots--;
	}
}

//Prevents epic bots from automatically joining the game
function bool NeedPlayers()
{
	return false;
}

//Prevents epic bots from automatically joining the game
function bool BecomeSpectator(PlayerController P)
{
	if ( !Super.BecomeSpectator(P) )
		return false;

	return true;
}

defaultproperties
{
	DamageModifier=1
	MaxSkill=7
	bAllowControlServer=True
	GameClass="BotDeathMatch"
	BotServerPort=3000
	ControlServerPort=3001
	NetWait=2
	CountDown=0
	DefaultEnemyRosterClass="XGame.xDMRoster"
	bPauseable=True
	HUDType="xInterface.HudCDeathmatch"
	PlayerControllerClassName="BotAPI.GBxPlayer"
	DefaultPlayerClassName="BotAPI.GBxPawn"
	GameName="Remote Bot DeathMatch"
	ScreenShotName="UT2004Thumbnails.DMShots"
	DecoTextName="XGame.DeathMatch"
	Acronym="DM"
}
